import * as R from "ramda"

// export type TagedData<T> = TagedDataItem<T>[]

export type NamedMap<T> = Map<string,T>

export const asMap = <A>(nameProvider:(v:A)=>string)=>(values:A[])=> new Map(values.map(v=>[nameProvider(v),v]))
export const map = <T,D>(func:(v:T)=>D)=>(namedMap:NamedMap<T>) => 
    new Map(Array.from(namedMap).map(([key,value])=>[key,func(value)]))

// (pairMapper,singleMapper)=>a[]=>b[]
export const pairwiseMap = <S,D>(pairMapper:(v1:S,v2:S)=>D, singleMapper:(v:S)=>D)=>(values : S[]):D[]=>{
    if(values.length===1) return [singleMapper(values[0])]
    else return R.aperture(2,values).map(x=>pairMapper(...x))
}
export const increment = pairwiseMap((v1:number,v2:number)=>v2-v1,(v:number)=>v)

export const takeTail = (num:number, defaultValue:number)=><T>(values:T[])=> [
   ...Array(num<=values.length?0:num-values.length).fill(defaultValue), ...R.take(num,values.reverse()).reverse()
]

export const addArray = (a1:number[],a2:number[])=>
    R.range(0,a1.length).map(i=>a1[i]+a2[i])


export const trace = <T>(x:T)=>{console.log(x);return x}

//[a]->[b]->[[a,b]]
//两个集合的笛卡尔积
type Pair<A,B> = [A,B]
export const cartesian = <A,B>(as:A[],bs:B[]):Pair<A,B>[]=>
    R.chain(a=>bs.reduce((s:Pair<A,B>[],b:B)=>([[a,b] as Pair<A,B>,...s]),[]),as)
    //as.map(a=>bs.reduce((s:Pair<A,B>[],b:B)=>([[a,b] as Pair<A,B>,...s]),[]))
    //     .reduce((s,v)=>[...s,...v],[])
    
export const recursiveOf = <T>(f:(x:T)=>T, init:T) => {
    return (function*(){
        let cur = init
        while(true){
            yield cur
            cur = f(cur)
        }
    })
}

export const take = (n:number)=><T>(i:Iterator<T>):T[] =>{
    var result :T[] = []
    var index = 0;
    while(true){
        const v = i.next()
        if(v.done || index++ >=n) break;
        else result.push(v.value)
    }
    return result;
}    

export const filter = <T>(pred:(v:T)=>boolean,i:Iterator<T>)  =>{
    return (function*(){
        while(true){
            const v = i.next();
            if(v.done) break;
            if(!pred(v.value)) break;
            yield v.value
        }
    })()
}
export function* forever(){
    var cur = 0;
    while(true){
        yield cur++
    }
}

export function* pairs(x:[number,number],y:(x:number)=>[number,number]){
    for(let a = x[0]; a<=x[1];a++){
        let yrange = y(a)
        for(let b = yrange[0]; b<=yrange[1] ;b++)
            yield {x:a,y:b}
    }
}

export const square = (x: number) => x * x
export const sqrt = Math.sqrt

export const add = (x:number, y:number) => x + y

export const log = <T>(v:T)=>{
     console.log(JSON.stringify(v))
     return v
 }